/*
*	SDL Graphics Extension
*	Pixel, surface and color functions
*
*	Started 990815 (split from sge_draw 010611)
*
*	License: LGPL v2+ (see the file LICENSE)
*	(c)1999-2003 Anders Lindstr�m
*/

/* MODIFIED: _PutPixelAlpha 02/27/2004 Janson
 *           All changes are for 32bpp surfaces only (only ones we use right now)
 *           1) 32bit uses long long to prevent clipping of alpha
 *           2) alpha blend for RGB is scaled towards alpha of pixel, so that
 *              the resulting pixel doesn't "double up" on alpha due to
 *              both the color being shaded AND alpha being present
 *           3) drawing on fully transparent surface just blends alpha, but
 *              doesn't have to scale it then (this is just an optimization)
 *
 * These modifications make all AA and alpha-blended functions graphic-program quality.
 *
 * MODIFIED: Prefix ++/-- instead of postfix (consistent with my other code)
 *           Removed all 8/24 bit code
 *
 * REMOVED: _sge_update and all related functions
 *          sge_CreateAlphaSurface
 *          all palette, copy, block, fill functions
 */

/*********************************************************************
 *  This library is free software; you can redistribute it and/or    *
 *  modify it under the terms of the GNU Library General Public      *
 *  License as published by the Free Software Foundation; either     *
 *  version 2 of the License, or (at your option) any later version. *
 *********************************************************************/

/*
*  Some of this code is taken from the "Introduction to SDL" and
*  John Garrison's PowerPak	
*/

// (MODIFIED Janson - using std header)
#include "all.h"

/* Globals used for sge_Lock */
Uint8 _sge_lock=1;


/**********************************************************************************/
/**                            Misc. functions                                   **/
/**********************************************************************************/

//==================================================================================
// Turns off automatic locking of surfaces
//==================================================================================
void sge_Lock_OFF(void)
{
	_sge_lock=0;	
}

//==================================================================================
// Turns on automatic locking (default)
//==================================================================================
void sge_Lock_ON(void)
{
	_sge_lock=1;	
}

//==================================================================================
// Returns locking mode (1-on and 0-off)
//==================================================================================
Uint8 sge_getLock(void)
{
  return _sge_lock;
}

//==================================================================================
// Returns the Uint32 color value for a 32bit (8/8/8/8) alpha surface
//==================================================================================
Uint32 sge_MapAlpha(Uint8 R, Uint8 G, Uint8 B, Uint8 A)
{
	Uint32 color=0;
	
	color|=R<<24;
	color|=G<<16;
	color|=B<<8;
	color|=A;
	
	return color;
}


//==================================================================================
// Sets an SDL error string
// Accepts formated argument - like printf()
// SDL_SetError() also does this, but it does not use standard syntax (why?)
//==================================================================================
void sge_SetError(const char *format, ...)
{
	char buf[256];

	va_list ap;
	
	#ifdef __WIN32__
	va_start(ap, format); //Stupid w32 crosscompiler
	#else
	va_start(ap, format);
	#endif
	
	vsprintf(buf, format, ap);
	va_end(ap);

	SDL_SetError(buf);
}



/**********************************************************************************/
/**                            Pixel functions                                   **/
/**********************************************************************************/

//==================================================================================
// Fast put pixel
//==================================================================================
void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
{
	if(x>=sge_clip_xmin(surface) && x<=sge_clip_xmax(surface) && y>=sge_clip_ymin(surface) && y<=sge_clip_ymax(surface)){
		switch (surface->format->BytesPerPixel) {
			case 2: { /* Probably 15-bpp or 16-bpp */
				*((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
			}
			break;

			case 4: { /* Probably 32-bpp */
				*((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
			}
			break;
		}
	}

}


//==================================================================================
// Fast put pixel (RGB)
//==================================================================================
void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint8 R, Uint8 G, Uint8 B)
{
	_PutPixel(surface,x,y, SDL_MapRGB(surface->format, R, G, B));
}


//==================================================================================
// Fastest put pixel functions (don't mess up indata, thank you)
//==================================================================================
void _PutPixel16(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
{
	*((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
}
void _PutPixel32(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
{
	*((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
}
void _PutPixelX(SDL_Surface *dest,Sint16 x,Sint16 y,Uint32 color)
{
	switch ( dest->format->BytesPerPixel ) {
	case 2:
		*((Uint16 *)dest->pixels + y*dest->pitch/2 + x) = color;
		break;
	case 4:
		*((Uint32 *)dest->pixels + y*dest->pitch/4 + x) = color;
		break;
	}
}


//==================================================================================
// Safe put pixel
//==================================================================================
void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
{

	if ( SDL_MUSTLOCK(surface) && _sge_lock ) {
		if ( SDL_LockSurface(surface) < 0 ) {
			return;
		}
	}

	_PutPixel(surface, x, y, color);

	if ( SDL_MUSTLOCK(surface) && _sge_lock ) {
		SDL_UnlockSurface(surface);
	}
}


//==================================================================================
// Safe put pixel (RGB)
//==================================================================================
void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint8 R, Uint8 G, Uint8 B)
{
  sge_PutPixel(surface,x,y, SDL_MapRGB(surface->format, R, G, B));
}


//==================================================================================
// Calculate y pitch offset
// (the y pitch offset is constant for the same y coord and surface)
//==================================================================================
Sint32 sge_CalcYPitch(SDL_Surface *dest,Sint16 y)
{
	if(y>=sge_clip_ymin(dest) && y<=sge_clip_ymax(dest)){
		switch ( dest->format->BytesPerPixel ) {
		case 2:
			return y*dest->pitch/2;
			break;
		case 4:
			return y*dest->pitch/4;
			break;
		}
	}
	
	return -1;
}


//==================================================================================
// Put pixel with precalculated y pitch offset
//==================================================================================
void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch, Uint32 color)
{
	if(x>=sge_clip_xmin(surface) && x<=sge_clip_xmax(surface) && ypitch>=0){
		switch (surface->format->BytesPerPixel) {
			case 2: { /* Probably 15-bpp or 16-bpp */
				*((Uint16 *)surface->pixels + ypitch + x) = color;
			}
			break;

			case 4: { /* Probably 32-bpp */
				*((Uint32 *)surface->pixels + ypitch + x) = color;
			}
			break;
		}
	}
}


//==================================================================================
// Get pixel
//==================================================================================
Uint32 sge_GetPixel(SDL_Surface *surface, Sint16 x, Sint16 y)
{
	if(x<0 || x>=surface->w || y<0 || y>=surface->h)
		return 0;

	switch (surface->format->BytesPerPixel) {
		case 2: { /* Probably 15-bpp or 16-bpp */
			return *((Uint16 *)surface->pixels + y*surface->pitch/2 + x);
		}
		break;

		case 4: { /* Probably 32-bpp */
			return *((Uint32 *)surface->pixels + y*surface->pitch/4 + x);
		}
		break;
	}
	return 0;
}


//==================================================================================
// Put pixel with alpha blending
//==================================================================================
void _PutPixelAlpha(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color, Uint8 alpha)
{
	if(x>=sge_clip_xmin(surface) && x<=sge_clip_xmax(surface) && y>=sge_clip_ymin(surface) && y<=sge_clip_ymax(surface)){
		Uint32 Rmask = surface->format->Rmask, Gmask = surface->format->Gmask, Bmask = surface->format->Bmask, Amask = surface->format->Amask;
		Uint32 R,G,B,A=0;

		switch (surface->format->BytesPerPixel) {
			case 2: { /* Probably 15-bpp or 16-bpp */		
				if( alpha == 255 ){
					*((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
				}else{
					Uint16 *pixel = (Uint16 *)surface->pixels + y*surface->pitch/2 + x;
					Uint32 dc = *pixel;
				
					R = ((dc & Rmask) + (( (color & Rmask) - (dc & Rmask) ) * alpha >> 8)) & Rmask;
					G = ((dc & Gmask) + (( (color & Gmask) - (dc & Gmask) ) * alpha >> 8)) & Gmask;
					B = ((dc & Bmask) + (( (color & Bmask) - (dc & Bmask) ) * alpha >> 8)) & Bmask;
					if( Amask )
						A = ((dc & Amask) + (( (color & Amask) - (dc & Amask) ) * alpha >> 8)) & Amask;

					*pixel= R | G | B | A;
				}
			}
			break;

			case 4: { /* Probably 32-bpp */
				Uint32 *pixel = (Uint32 *)surface->pixels + y*surface->pitch/4 + x;
				// Special case 100% blend and 0% destination
				if( alpha == 255 ){
					*pixel = color;
                }else if ((Amask) && ((*pixel & Amask) == 0)) {
                    // Destination is 100% transparent, so place our pixel normal with alpha scaled
                    A = ((long long)(color & Amask) * alpha >> 8) & Amask;
                    *pixel = (color & (Rmask | Gmask | Bmask)) | A;
				}else{
    				Uint32 dc = *pixel;
    				
            		// Scale RGB blend based on alpha of destination
                    int destAlpha = (dc & Amask) >> surface->format->Ashift;
                    int rgbScale = alpha;
                    if (destAlpha < alpha) rgbScale = alpha + (256 - alpha) * (alpha - destAlpha) / 255;
					
					// We have to ensure no unwanted clipping occurs, of both overflow and negatives
					// Our main() asserts that long long is at least 48 bits
				    R = ((dc & Rmask) + (((long long)(color & Rmask) - (long long)(dc & Rmask) ) * rgbScale >> 8)) & Rmask;
					G = ((dc & Gmask) + (( (color & Gmask) - (dc & Gmask) ) * rgbScale >> 8)) & Gmask;
					B = ((dc & Bmask) + (( (color & Bmask) - (dc & Bmask) ) * rgbScale >> 8)) & Bmask;
					if (Amask)
                        A = ((dc & Amask) + (((long long)(color & Amask) - (long long)(dc & Amask) ) * alpha >> 8)) & Amask;
					*pixel = R | G | B | A;
				}
			}
			break;
		}
	}
}

void sge_PutPixelAlpha(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color, Uint8 alpha)
{
	if ( SDL_MUSTLOCK(surface) && _sge_lock )
		if ( SDL_LockSurface(surface) < 0 )
			return;

	_PutPixelAlpha(surface,x,y,color,alpha);
	
	/* unlock the display */
	if (SDL_MUSTLOCK(surface) && _sge_lock) {
		SDL_UnlockSurface(surface);
	}
}


void _PutPixelAlpha(SDL_Surface *surface, Sint16 x, Sint16 y, Uint8 R, Uint8 G, Uint8 B, Uint8 alpha)
{
  _PutPixelAlpha(surface,x,y, SDL_MapRGB(surface->format, R, G, B),alpha);
}
void sge_PutPixelAlpha(SDL_Surface *surface, Sint16 x, Sint16 y, Uint8 R, Uint8 G, Uint8 B, Uint8 alpha)
{
  sge_PutPixelAlpha(surface,x,y, SDL_MapRGB(surface->format, R, G, B), alpha);
}



/**********************************************************************************/
/**                       Blitting/surface functions                             **/
/**********************************************************************************/

//==================================================================================
// Clear surface to color
//==================================================================================
void sge_ClearSurface(SDL_Surface *Surface, Uint32 color)
{

	SDL_FillRect(Surface, (SDL_Rect*)NULL, color);
}


//==================================================================================
// Clear surface to color (RGB)
//==================================================================================
void sge_ClearSurface(SDL_Surface *Surface, Uint8 R, Uint8 G, Uint8 B)
{
	sge_ClearSurface(Surface,SDL_MapRGB(Surface->format, R, G, B));
}

